package cn.iocoder.yudao.module.tms.service.accountperiodtodo;

import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.tms.controller.admin.accountperiodtodo.vo.AccountPeriodTodoPageReqVO;
import cn.iocoder.yudao.module.tms.controller.admin.accountperiodtodo.vo.AccountPeriodTodoRespVO;
import cn.iocoder.yudao.module.tms.controller.admin.accountperiodtodo.vo.AccountPeriodTodoStatisticsRespVO;
import cn.iocoder.yudao.module.tms.dal.dataobject.accountperiodtodo.AccountPeriodTodoDO;
import cn.iocoder.yudao.module.tms.dal.dataobject.companyaccountperiod.CompanyAccountPeriodDO;
import cn.iocoder.yudao.module.tms.dal.mysql.accountperiodtodo.AccountPeriodTodoMapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.List;

/**
 * 账期待办事项 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Slf4j
public class AccountPeriodTodoServiceImpl implements AccountPeriodTodoService {

    @Resource
    private AccountPeriodTodoMapper accountPeriodTodoMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void checkAndGenerateTodos(CompanyAccountPeriodDO accountPeriod) {
        // 1. 检查授信预警
        checkCreditAlert(accountPeriod);
        
        // 2. 检查账期预警
        checkBillingAlert(accountPeriod);
    }

    /**
     * 检查授信预警
     */
    private void checkCreditAlert(CompanyAccountPeriodDO accountPeriod) {
        BigDecimal currentUsed = accountPeriod.getCurrentCreditUsed();
        BigDecimal alertAmount = accountPeriod.getCreditAlertAmount();
        
        if (currentUsed != null && alertAmount != null && currentUsed.compareTo(alertAmount) >= 0) {
            // 检查是否已存在相同类型的待处理待办
            if (!hasPendingTodo(accountPeriod.getId(), AccountPeriodTodoDO.TodoTypeEnum.CREDIT_ALERT.name())) {
                generateCreditAlertTodo(accountPeriod);
            }
        }
    }

    /**
     * 检查账期预警
     */
    private void checkBillingAlert(CompanyAccountPeriodDO accountPeriod) {
        if (accountPeriod.getBillingDueDate() == null) {
            return;
        }
        
        LocalDate today = LocalDate.now();
        LocalDate dueDate = accountPeriod.getBillingDueDate();
        long remainingDays = ChronoUnit.DAYS.between(today, dueDate);
        
        if (remainingDays < 0) {
            // 已逾期
            if (!hasPendingTodo(accountPeriod.getId(), AccountPeriodTodoDO.TodoTypeEnum.BILLING_OVERDUE.name())) {
                generateBillingOverdueTodo(accountPeriod, (int) Math.abs(remainingDays));
            }
        } else if (remainingDays <= accountPeriod.getBillingAlertDays()) {
            // 即将到期
            if (!hasPendingTodo(accountPeriod.getId(), AccountPeriodTodoDO.TodoTypeEnum.BILLING_ALERT.name())) {
                generateBillingAlertTodo(accountPeriod, (int) remainingDays);
            }
        }
    }

    /**
     * 检查是否存在待处理的待办
     */
    private boolean hasPendingTodo(Long periodId, String todoType) {
        LambdaQueryWrapperX<AccountPeriodTodoDO> query = new LambdaQueryWrapperX<>();
        query.eq(AccountPeriodTodoDO::getPeriodId, periodId)
             .eq(AccountPeriodTodoDO::getTodoType, todoType)
             .eq(AccountPeriodTodoDO::getStatus, AccountPeriodTodoDO.TodoStatusEnum.PENDING.name());
        return accountPeriodTodoMapper.selectCount(query) > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void generateCreditAlertTodo(CompanyAccountPeriodDO accountPeriod) {
        AccountPeriodTodoDO todo = AccountPeriodTodoDO.builder()
            .periodId(accountPeriod.getId())
            .companyId(accountPeriod.getCompanyId())
            .companyName(getCompanyName(accountPeriod.getCompanyId()))
            .todoType(AccountPeriodTodoDO.TodoTypeEnum.CREDIT_ALERT.name())
            .title(String.format("【授信预警】%s 合作金额超过预警值", getCompanyName(accountPeriod.getCompanyId())))
            .content(generateCreditAlertContent(accountPeriod))
            .priority(AccountPeriodTodoDO.PriorityEnum.HIGH.name())
            .status(AccountPeriodTodoDO.TodoStatusEnum.PENDING.name())
            .alertAmount(accountPeriod.getCreditAlertAmount())
            .currentAmount(accountPeriod.getCurrentCreditUsed())
            .dueDate(LocalDateTime.now().plusDays(3)) // 3天内处理
            .build();
        
        accountPeriodTodoMapper.insert(todo);
        log.info("[generateCreditAlertTodo] 生成授信预警待办: periodId={}, companyId={}", 
            accountPeriod.getId(), accountPeriod.getCompanyId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void generateBillingAlertTodo(CompanyAccountPeriodDO accountPeriod, int remainingDays) {
        // 将LocalDate转换为LocalDateTime（设置为当天结束时间23:59:59）
        LocalDateTime dueDateTime = accountPeriod.getBillingDueDate() != null 
            ? accountPeriod.getBillingDueDate().atTime(LocalTime.MAX) 
            : null;
        
        AccountPeriodTodoDO todo = AccountPeriodTodoDO.builder()
            .periodId(accountPeriod.getId())
            .companyId(accountPeriod.getCompanyId())
            .companyName(getCompanyName(accountPeriod.getCompanyId()))
            .todoType(AccountPeriodTodoDO.TodoTypeEnum.BILLING_ALERT.name())
            .title(String.format("【账期提醒】%s 账期即将到期", getCompanyName(accountPeriod.getCompanyId())))
            .content(generateBillingAlertContent(accountPeriod, remainingDays))
            .priority(AccountPeriodTodoDO.PriorityEnum.NORMAL.name())
            .status(AccountPeriodTodoDO.TodoStatusEnum.PENDING.name())
            .currentAmount(accountPeriod.getCurrentCreditUsed())
            .remainingDays(remainingDays)
            .dueDate(dueDateTime)
            .build();
        
        accountPeriodTodoMapper.insert(todo);
        log.info("[generateBillingAlertTodo] 生成账期预警待办: periodId={}, remainingDays={}", 
            accountPeriod.getId(), remainingDays);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void generateBillingOverdueTodo(CompanyAccountPeriodDO accountPeriod, int overdueDays) {
        AccountPeriodTodoDO todo = AccountPeriodTodoDO.builder()
            .periodId(accountPeriod.getId())
            .companyId(accountPeriod.getCompanyId())
            .companyName(getCompanyName(accountPeriod.getCompanyId()))
            .todoType(AccountPeriodTodoDO.TodoTypeEnum.BILLING_OVERDUE.name())
            .title(String.format("【账期逾期】%s 账期已逾期 %d 天", getCompanyName(accountPeriod.getCompanyId()), overdueDays))
            .content(generateBillingOverdueContent(accountPeriod, overdueDays))
            .priority(AccountPeriodTodoDO.PriorityEnum.URGENT.name())
            .status(AccountPeriodTodoDO.TodoStatusEnum.PENDING.name())
            .currentAmount(accountPeriod.getCurrentCreditUsed())
            .remainingDays(-overdueDays)
            .dueDate(LocalDateTime.now()) // 立即处理
            .build();
        
        accountPeriodTodoMapper.insert(todo);
        log.info("[generateBillingOverdueTodo] 生成账期逾期待办: periodId={}, overdueDays={}", 
            accountPeriod.getId(), overdueDays);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void completeTodo(Long id, String remark) {
        AccountPeriodTodoDO todo = accountPeriodTodoMapper.selectById(id);
        if (todo == null) {
            throw new IllegalArgumentException("待办事项不存在");
        }
        
        todo.setStatus(AccountPeriodTodoDO.TodoStatusEnum.COMPLETED.name());
        todo.setCompletedTime(LocalDateTime.now());
        todo.setCompletedBy(SecurityFrameworkUtils.getLoginUserId());
        todo.setCompletedRemark(remark);
        
        accountPeriodTodoMapper.updateById(todo);
        log.info("[completeTodo] 完成待办事项: id={}, remark={}", id, remark);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelTodo(Long id, String reason) {
        AccountPeriodTodoDO todo = accountPeriodTodoMapper.selectById(id);
        if (todo == null) {
            throw new IllegalArgumentException("待办事项不存在");
        }
        
        todo.setStatus(AccountPeriodTodoDO.TodoStatusEnum.CANCELLED.name());
        todo.setCompletedRemark(reason);
        
        accountPeriodTodoMapper.updateById(todo);
        log.info("[cancelTodo] 取消待办事项: id={}, reason={}", id, reason);
    }

    /**
     * 生成授信预警内容
     */
    private String generateCreditAlertContent(CompanyAccountPeriodDO accountPeriod) {
        return String.format(
            "公司：%s\n" +
            "授信额度：%.2f 元\n" +
            "预警金额：%.2f 元\n" +
            "当前使用：%.2f 元\n" +
            "超出金额：%.2f 元\n\n" +
            "请及时联系客户进行结算或调整授信额度。",
            getCompanyName(accountPeriod.getCompanyId()),
            accountPeriod.getCreditLimit(),
            accountPeriod.getCreditAlertAmount(),
            accountPeriod.getCurrentCreditUsed(),
            accountPeriod.getCurrentCreditUsed().subtract(accountPeriod.getCreditAlertAmount())
        );
    }

    /**
     * 生成账期预警内容
     */
    private String generateBillingAlertContent(CompanyAccountPeriodDO accountPeriod, int remainingDays) {
        return String.format(
            "公司：%s\n" +
            "账期天数：%d 天\n" +
            "到期日期：%s\n" +
            "剩余天数：%d 天\n" +
            "当前欠款：%.2f 元\n\n" +
            "请提前通知客户准备结算款项。",
            getCompanyName(accountPeriod.getCompanyId()),
            accountPeriod.getBillingCycleDays(),
            accountPeriod.getBillingDueDate(),
            remainingDays,
            accountPeriod.getCurrentCreditUsed()
        );
    }

    /**
     * 生成账期逾期内容
     */
    private String generateBillingOverdueContent(CompanyAccountPeriodDO accountPeriod, int overdueDays) {
        return String.format(
            "公司：%s\n" +
            "到期日期：%s\n" +
            "逾期天数：%d 天\n" +
            "当前欠款：%.2f 元\n\n" +
            "⚠️ 账期已逾期，请立即联系客户催收款项！",
            getCompanyName(accountPeriod.getCompanyId()),
            accountPeriod.getBillingDueDate(),
            overdueDays,
            accountPeriod.getCurrentCreditUsed()
        );
    }

    /**
     * 获取公司名称
     * TODO: 从公司基础信息表获取
     */
    private String getCompanyName(Long companyId) {
        return "公司-" + companyId;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchCompleteTodo(List<Long> ids, String remark) {
        for (Long id : ids) {
            completeTodo(id, remark);
        }
        log.info("[batchCompleteTodo] 批量完成待办: ids={}, count={}", ids, ids.size());
    }

    @Override
    public PageResult<AccountPeriodTodoRespVO> getAccountPeriodTodoPage(AccountPeriodTodoPageReqVO pageReqVO) {
        // 构建查询条件
        LambdaQueryWrapperX<AccountPeriodTodoDO> query = new LambdaQueryWrapperX<>();
        query.eqIfPresent(AccountPeriodTodoDO::getCompanyId, pageReqVO.getCompanyId())
             .likeIfPresent(AccountPeriodTodoDO::getCompanyName, pageReqVO.getCompanyName())
             .eqIfPresent(AccountPeriodTodoDO::getTodoType, pageReqVO.getTodoType())
             .eqIfPresent(AccountPeriodTodoDO::getStatus, pageReqVO.getStatus())
             .eqIfPresent(AccountPeriodTodoDO::getPriority, pageReqVO.getPriority())
             .eqIfPresent(AccountPeriodTodoDO::getAssigneeId, pageReqVO.getAssigneeId())
             .betweenIfPresent(AccountPeriodTodoDO::getDueDate, pageReqVO.getDueDateStart(), pageReqVO.getDueDateEnd())
             .orderByDesc(AccountPeriodTodoDO::getCreateTime);
        
        // 分页查询
        PageResult<AccountPeriodTodoDO> pageResult = accountPeriodTodoMapper.selectPage(pageReqVO, query);
        
        // 转换为VO
        return BeanUtils.toBean(pageResult, AccountPeriodTodoRespVO.class);
    }

    @Override
    public AccountPeriodTodoRespVO getAccountPeriodTodo(Long id) {
        AccountPeriodTodoDO todo = accountPeriodTodoMapper.selectById(id);
        if (todo == null) {
            throw new IllegalArgumentException("待办事项不存在");
        }
        return BeanUtils.toBean(todo, AccountPeriodTodoRespVO.class);
    }

    @Override
    public AccountPeriodTodoStatisticsRespVO getStatistics() {
        AccountPeriodTodoStatisticsRespVO statistics = new AccountPeriodTodoStatisticsRespVO();
        
        // 总数
        Long totalCount = accountPeriodTodoMapper.selectCount();
        statistics.setTotalCount(totalCount.intValue());
        
        // 各状态数量
        LambdaQueryWrapperX<AccountPeriodTodoDO> pendingQuery = new LambdaQueryWrapperX<>();
        pendingQuery.eq(AccountPeriodTodoDO::getStatus, AccountPeriodTodoDO.TodoStatusEnum.PENDING.name());
        statistics.setPendingCount(accountPeriodTodoMapper.selectCount(pendingQuery).intValue());
        
        LambdaQueryWrapperX<AccountPeriodTodoDO> processingQuery = new LambdaQueryWrapperX<>();
        processingQuery.eq(AccountPeriodTodoDO::getStatus, AccountPeriodTodoDO.TodoStatusEnum.PROCESSING.name());
        statistics.setProcessingCount(accountPeriodTodoMapper.selectCount(processingQuery).intValue());
        
        LambdaQueryWrapperX<AccountPeriodTodoDO> completedQuery = new LambdaQueryWrapperX<>();
        completedQuery.eq(AccountPeriodTodoDO::getStatus, AccountPeriodTodoDO.TodoStatusEnum.COMPLETED.name());
        statistics.setCompletedCount(accountPeriodTodoMapper.selectCount(completedQuery).intValue());
        
        LambdaQueryWrapperX<AccountPeriodTodoDO> cancelledQuery = new LambdaQueryWrapperX<>();
        cancelledQuery.eq(AccountPeriodTodoDO::getStatus, AccountPeriodTodoDO.TodoStatusEnum.CANCELLED.name());
        statistics.setCancelledCount(accountPeriodTodoMapper.selectCount(cancelledQuery).intValue());
        
        // 完成率
        if (statistics.getTotalCount() > 0) {
            statistics.setCompletionRate(
                (double) statistics.getCompletedCount() / statistics.getTotalCount() * 100
            );
        } else {
            statistics.setCompletionRate(0.0);
        }
        
        // 紧急待办数量
        LambdaQueryWrapperX<AccountPeriodTodoDO> urgentQuery = new LambdaQueryWrapperX<>();
        urgentQuery.eq(AccountPeriodTodoDO::getPriority, AccountPeriodTodoDO.PriorityEnum.URGENT.name())
                   .in(AccountPeriodTodoDO::getStatus, Arrays.asList(
                       AccountPeriodTodoDO.TodoStatusEnum.PENDING.name(),
                       AccountPeriodTodoDO.TodoStatusEnum.PROCESSING.name()
                   ));
        statistics.setUrgentCount(accountPeriodTodoMapper.selectCount(urgentQuery).intValue());
        
        // 逾期待办数量（剩余天数<0）
        LambdaQueryWrapperX<AccountPeriodTodoDO> overdueQuery = new LambdaQueryWrapperX<>();
        overdueQuery.lt(AccountPeriodTodoDO::getRemainingDays, 0)
                    .in(AccountPeriodTodoDO::getStatus, Arrays.asList(
                        AccountPeriodTodoDO.TodoStatusEnum.PENDING.name(),
                        AccountPeriodTodoDO.TodoStatusEnum.PROCESSING.name()
                    ));
        statistics.setOverdueCount(accountPeriodTodoMapper.selectCount(overdueQuery).intValue());
        
        // 平均处理时长（小时）
        // 查询已完成的待办，计算平均处理时长
        LambdaQueryWrapperX<AccountPeriodTodoDO> completedTodosQuery = new LambdaQueryWrapperX<>();
        completedTodosQuery.eq(AccountPeriodTodoDO::getStatus, AccountPeriodTodoDO.TodoStatusEnum.COMPLETED.name())
                          .isNotNull(AccountPeriodTodoDO::getCompletedTime);
        List<AccountPeriodTodoDO> completedTodos = accountPeriodTodoMapper.selectList(completedTodosQuery);
        
        if (!completedTodos.isEmpty()) {
            double totalHours = 0;
            for (AccountPeriodTodoDO todo : completedTodos) {
                if (todo.getCreateTime() != null && todo.getCompletedTime() != null) {
                    long hours = ChronoUnit.HOURS.between(todo.getCreateTime(), todo.getCompletedTime());
                    totalHours += hours;
                }
            }
            statistics.setAvgProcessingHours(totalHours / completedTodos.size());
        } else {
            statistics.setAvgProcessingHours(0.0);
        }
        
        return statistics;
    }

    @Override
    public Integer getMyTodoCount() {
        Long userId = SecurityFrameworkUtils.getLoginUserId();
        if (userId == null) {
            return 0;
        }
        
        LambdaQueryWrapperX<AccountPeriodTodoDO> query = new LambdaQueryWrapperX<>();
        query.eq(AccountPeriodTodoDO::getAssigneeId, userId)
             .eq(AccountPeriodTodoDO::getStatus, AccountPeriodTodoDO.TodoStatusEnum.PENDING.name());
        
        return accountPeriodTodoMapper.selectCount(query).intValue();
    }
}
